<!--
 * @Author       : wfl
 * @LastEditors: ww
 * @description  :
 * @updateInfo   :
 * @Date         : 2022-11-23 15:19:42
 * @LastEditTime: 2023-06-30 15:32:45
-->
<script lang="ts" name="ProcessTree">
import { computed, h, ref } from 'vue'

import { EInsertNode, ENode } from '@main/views/approve-workflow/enum'
import { _, buildUUID } from 'iking-utils'
import { ElButton, ElMessage } from 'element-plus'

// 导入所有节点组件
import Approve from '@main/views/approve-workflow/WorkFlow/tree-node/ApprovalNode.vue'
import Carbon_copy from '@main/views/approve-workflow/WorkFlow/tree-node/CcNode.vue'
import Concurrent from '@main/views/approve-workflow/WorkFlow/tree-node/ConcurrentNode.vue'
import Condition from '@main/views/approve-workflow/WorkFlow/tree-node/ConditionNode.vue'
import Trigger from '@main/views/approve-workflow/WorkFlow/tree-node/TriggerNode.vue'
import Delay from '@main/views/approve-workflow/WorkFlow/tree-node/DelayNode.vue'
import Empty from '@main/views/approve-workflow/WorkFlow/tree-node/EmptyNode.vue'
import Initiator from '@main/views/approve-workflow/WorkFlow/tree-node/RootNode.vue'
import Node from '@main/views/approve-workflow/WorkFlow/tree-node/Node.vue'
import Dispose from '@main/views/approve-workflow/WorkFlow/tree-node/DisposeNode.vue'
import {
  APPROVAL_PROPS,
  CC_PROPS,
  CONDITION_PROPS,
  DELAY_PROPS,
  DISPOSE_PROPS,
  TRIGGER_PROPS
} from './DefaultNodeProps'

interface ITreeNode {
  children: any
  branchs: any[]
  id: string | number
  type: string
}

export default {
  components: {
    Approve,
    CarbonCopy: Carbon_copy,
    Concurrent,
    Condition,
    Trigger,
    Delay,
    Empty,
    Initiator,
    Node,
    ElButton,
    Dispose
  },
  emits: ['selectedNode'],
  setup(props, { emit, expose }) {
    const valid = ref(true)
    const flowStore = useFlowStore()
    const nodeMap = computed(() => flowStore.nodeMap)
    const dom = computed(() => flowStore.design.process)

    const refRoot = ref()
    const getDomTree = (h: any, node: ITreeNode) => {
      toMapping(node)
      if (isPrimaryNode(node)) {
        // 普通业务节点
        const childDoms: any = getDomTree(h, node.children)

        decodeAppendDom(h, node, childDoms)
        return [h('div', { class: { 'primary-node': true } }, childDoms)]
      }
      else if (isBranchNode(node)) {
        let index = 0
        // 遍历分支节点，包含并行及条件节点
        const branchItems: any = node.branchs?.map(branchNode => {
          // 处理每个分支内子节点
          toMapping(branchNode)
          const childDoms: any = getDomTree(h, branchNode.children)
          decodeAppendDom(h, branchNode, childDoms, {
            level: index + 1,
            size: node.branchs.length
          })
          // 插入4条横线，遮挡掉条件节点左右半边线条
          insertCoverLine(h, index, childDoms, node.branchs)
          // 遍历子分支尾部分支
          index++
          return h('div', { class: { 'branch-node-item': true } }, childDoms)
        })
        // 插入添加分支/条件的按钮
        branchItems?.unshift(
          h('div', { class: { 'add-branch-btn': true } }, [
            h(
              ElButton,
              {
                class: 'add-branch-btn-el',
                // size: "small",
                round: true,
                onClick: () => addBranchNode(node),
                innerHTML: `添加${isConditionNode(node) ? '条件' : '分支'}`
              },
              []
            )
          ])
        )
        const bchDom = [
          h('div', { class: { 'branch-node': true } }, branchItems)
        ]
        // 继续遍历分支后的节点
        const afterChildDoms: any = getDomTree(h, node.children)
        return [h('div', {}, [bchDom, afterChildDoms])]
      }
      else if (isEmptyNode(node)) {
        // 空节点，存在于分支尾部
        const childDoms: any = getDomTree(h, node.children)
        decodeAppendDom(h, node, childDoms)
        return [h('div', { class: { 'empty-node': true } }, childDoms)]
      }
      else {
        // 遍历到了末端，无子节点
        return []
      }
    }
    // 解码渲染的时候插入dom到同级
    const decodeAppendDom = (h: any, node: any, dom: any, props: any = {}) => {
      props.config = node
      let _com = null
      const opt = {
        ...props,
        ref: node.id,
        key: node.id,
        // 定义事件，插入节点，删除节点，选中节点，复制/移动
        // withModifiers((type: any) => insertNode(type, node), ['stop']) ,
        onInsertNode: (type: any) => {
          insertNode(type, node)
        },
        onDelNode: () => delNode(node),
        onSelected: () => selectNode(node),
        onCopy: () => copyBranch(node),
        onLeftMove: () => branchMove(node, -1),
        onRightMove: () => branchMove(node, 1)
      }
      switch (node.type.toUpperCase()) {
        case ENode.ROOT:
          _com = h(Initiator, _.cloneDeep(opt), [])
          break
        case ENode.APPROVAL:
          _com = h(Approve, _.cloneDeep(opt), [])
          break
        case ENode.DISPOSE:
          _com = h(Dispose, _.cloneDeep(opt), [])
          break
        case ENode.NODE:
          _com = h(Node, _.cloneDeep(opt), [])
          break
        case ENode.CC:
          _com = h(Carbon_copy, _.cloneDeep(opt), [])
          break
        case ENode.DELAY:
          _com = h(Delay, _.cloneDeep(opt), [])
          break
        case ENode.TRIGGER:
          _com = h(Trigger, _.cloneDeep(opt), [])
          break
        case ENode.CONDITION:
          _com = h(Condition, _.cloneDeep(opt), [])
          break
        case ENode.CONCURRENT:
          _com = h(Concurrent, _.cloneDeep(opt), [])
          break
        case ENode.EMPTY:
          _com = h(Empty, _.cloneDeep(opt), [])
          break
        default:
          _com = h(node.type.toLowerCase(), _.cloneDeep(opt), [])
          break
      }
      dom.unshift(_com)
    }
    // id映射到map，用来向上遍历
    const toMapping = (node: { children?: any; branchs?: any[]; id?: any; }) => {
      if (node && node.id)
        nodeMap.value.set(node.id, node)
    }
    const insertCoverLine = (h: any, index: any, doms: any, branchs: any) => {
      if (index === 0) {
        // 最左侧分支
        doms.unshift(h('div', { class: { 'line-top-left': true } }, []))
        doms.unshift(h('div', { class: { 'line-bot-left': true } }, []))
      }
      else if (index === branchs.length - 1) {
        // 最右侧分支
        doms.unshift(h('div', { class: { 'line-top-right': true } }, []))
        doms.unshift(h('div', { class: { 'line-bot-right': true } }, []))
      }
    }
    // 复制条件节点
    const copyBranch = (node: any) => {
      const parentNode = nodeMap.value.get(node.parentId)
      // 复制时 copy
      const branchNode = _.cloneDeep(node)
      branchNode.name = `${branchNode.name}-copy`
      forEachNode(
        parentNode,
        branchNode,
        (parent: { id: any; }, node: { id: string; parentId: any; }) => {
          const id = buildUUID()
          console.info(node, `新id =>${id}`, `老nodeId:${node.id}`)
          node.id = id
          node.parentId = parent.id
        }
      )
      parentNode.branchs?.splice(
        parentNode.branchs.indexOf(node),
        0,
        branchNode
      )
    }
    const branchMove = (node: { parentId: any; }, offset: number) => {
      const parentNode = nodeMap.value.get(node.parentId)
      const index = parentNode.branchs.indexOf(node)
      const branch = parentNode.branchs[index + offset]
      parentNode.branchs[index + offset] = parentNode.branchs[index]
      parentNode.branchs[index] = branch
      // this.$forceUpdate();
    }
    // 判断是否为主要业务节点
    const isPrimaryNode = (node: {
      children?: any
      branchs?: any[]
      type?: any
    }) => {
      return [
        ENode.ROOT,
        ENode.APPROVAL,
        ENode.CC,
        ENode.DELAY,
        ENode.TRIGGER,
        ENode.DISPOSE
      ].includes(node?.type)
    }
    const isBranchNode = (node: {
      children?: any
      branchs?: any[]
      type?: any
    }) => {
      return (
        node
        && (node.type === ENode.CONDITIONS || node.type === ENode.CONCURRENTS)
      )
    }
    const isEmptyNode = (node: {
      children?: any
      branchs?: any[]
      type?: any
    }) => {
      return node && node.type === ENode.EMPTY
    }
    // 是分支节点
    const isConditionNode = (node: {
      children?: any
      branchs?: any[]
      type?: any
    }) => {
      return node.type === ENode.CONDITIONS
    }
    // 是分支节点
    const isBranchSubNode = (node: ITreeNode) => {
      return (
        node
        && (node.type === ENode.CONDITION || node.type === ENode.CONCURRENT)
      )
    }
    // const isConcurrentNode = (node: { type: string }) => {
    //   return node.type === ENode.CONCURRENTS
    // }
    // 选中一个节点
    const selectNode = (node: any) => {
      flowStore.setSelectedNode(node)
      emit('selectedNode', node)
    }
    // 处理节点插入逻辑
    const insertNode = (
      type: any,
      parentNode: {
        children: {
          children?: any
          id: any
          parentId?: any
          props?: {}
          type?: any
        }
        id: any
      }
    ) => {
      refRoot.value?.click()
      // 缓存一下后面的节点
      const afterNode = parentNode.children
      // 插入新节点
      parentNode.children = {
        id: buildUUID(),
        parentId: parentNode.id,
        props: {},
        type
      }
      switch (type) {
        case EInsertNode.APPROVAL:
          // parentNode, afterNode
          insertApprovalNode(parentNode)
          break
        case EInsertNode.CC:
          insertCcNode(parentNode)
          break
        case EInsertNode.DELAY:
          insertDelayNode(parentNode)
          break
        case EInsertNode.TRIGGER:
          insertTriggerNode(parentNode)
          break
        case EInsertNode.CONDITIONS:
          insertConditionsNode(parentNode)
          break
        case EInsertNode.CONCURRENTS:
          insertConcurrentsNode(parentNode)
          break
        case EInsertNode.DISPOSE:
          insertDisposeNode(parentNode)
          break
        default:
          break
      }
      // 拼接后续节点
      if (isBranchNode({ type })) {
        if (afterNode && afterNode.id)
          afterNode.parentId = parentNode.children.children.id

        parentNode.children.children.children = afterNode
      }
      else {
        if (afterNode && afterNode.id)
          afterNode.parentId = parentNode.children.id

        parentNode.children.children = afterNode
      }
    }
    const handSetParent = (
      parentNode: { children: any; },
      name: any,
      props: any
    ) => {
      parentNode.children.name = name
      parentNode.children.props = _.cloneDeep(props)
    }
    const insertApprovalNode = (parentNode: { children: any; }) => {
      handSetParent(parentNode, '审批人', APPROVAL_PROPS)
    }
    const insertCcNode = (parentNode: { children: any; }) => {
      handSetParent(parentNode, '抄送人', CC_PROPS)
    }
    const insertDisposeNode = (parentNode: { children: any; }) => {
      handSetParent(parentNode, '办理人', DISPOSE_PROPS)
    }
    const insertDelayNode = (parentNode: { children: any; }) => {
      handSetParent(parentNode, '延时处理', DELAY_PROPS)
    }
    const insertTriggerNode = (parentNode: { children: any; }) => {
      handSetParent(parentNode, '触发器', TRIGGER_PROPS)
    }
    const insertConditionsNode = (parentNode: any) => {
      parentNode.children.name = '条件分支'
      parentNode.children.children = {
        id: buildUUID(),
        parentId: parentNode.children.id,
        type: 'EMPTY'
      }
      parentNode.children.branchs = [
        {
          id: buildUUID(),
          parentId: parentNode.children.id,
          type: 'CONDITION',
          props: _.cloneDeep(CONDITION_PROPS as any),
          name: '条件1',
          conditionOrder: 1,
          children: {}
        },
        {
          id: buildUUID(),
          parentId: parentNode.children.id,
          type: 'CONDITION',
          isDefault: true,
          props: _.cloneDeep(CONDITION_PROPS as any),
          name: '系统默认条件',
          conditionOrder: 99999,
          children: {}
        }
      ]
    }
    const insertConcurrentsNode = (parentNode: any) => {
      parentNode.children.name = '并行分支'
      parentNode.children.children = {
        id: buildUUID(),
        parentId: parentNode.children.id,
        type: 'EMPTY'
      }
      parentNode.children.branchs = [
        {
          id: buildUUID(),
          name: '分支1',
          parentId: parentNode.children.id,
          type: 'BRANCH',
          props: {},
          children: {}
        },
        {
          id: buildUUID(),
          name: '分支2',
          parentId: parentNode.children.id,
          type: 'BRANCH',
          props: {},
          children: {}
        }
      ]
    }
    const getBranchEndNode = (conditionNode: any): any => {
      if (!conditionNode.children || !conditionNode.children.id)
        return conditionNode as unknown as any

      return getBranchEndNode(conditionNode.children as any)
    }
    const addBranchNode = (node: {
      children?: any
      branchs: any
      id?: any
    }) => {
      const LEN = node.branchs.length
      if (LEN < 12) {
        const newBranch = {
          id: buildUUID(),
          parentId: node.id,
          name: (isConditionNode(node) ? '条件' : '分支') + LEN,
          props: isConditionNode(node) ? _.cloneDeep(CONDITION_PROPS as any) : {},
          type: isConditionNode(node) ? 'CONDITION' : 'BRANCH',
          conditionOrder: LEN,
          children: {}
        }
        // 每次向倒数第二项添加
        node.branchs.splice(LEN - 1, 0, newBranch)
      }
      else {
        ElMessage.warning('最多只能添加 12 项😥')
      }
    }
    // 删除当前节点
    const delNode = (node: {
      parentId: any
      children: { id: any; parentId: any; }
    }) => {
      console.info('删除节点', node)
      // 获取该节点的父节点
      const parentNode = nodeMap.value.get(node.parentId)
      if (parentNode) {
        // 判断该节点的父节点是不是分支节点
        if (isBranchNode(parentNode)) {
          // 移除该分支
          parentNode.branchs?.splice(parentNode.branchs.indexOf(node), 1)
          // 处理只剩1个分支的情况
          if (parentNode.branchs.length < 2) {
            // 获取条件组的父节点
            const ppNode = nodeMap.value.get(parentNode.parentId)
            // 判断唯一分支是否存在业务节点
            if (
              parentNode.branchs[0].children
              && parentNode.branchs[0].children.id
            ) {
              // 将剩下的唯一分支头部合并到主干
              ppNode.children = parentNode.branchs[0].children
              ppNode.children.parentId = ppNode.id
              // 搜索唯一分支末端最后一个节点
              const endNode: any = getBranchEndNode(parentNode.branchs[0])
              // 后续节点进行拼接, 这里要取EMPTY后的节点
              endNode.children = parentNode.children.children
              if (endNode.children && endNode.children.id)
                endNode.children.parentId = endNode.id
            }
            else {
              // 直接合并分支后面的节点，这里要取EMPTY后的节点
              ppNode.children = parentNode.children.children
              if (ppNode.children && ppNode.children.id)
                ppNode.children.parentId = ppNode.id
            }
          }
        }
        else {
          // 不是的话就直接删除
          if (node.children && node.children.id)
            node.children.parentId = parentNode.id

          parentNode.children = node.children
        }
        // this.$forceUpdate();
      }
      else {
        ElMessage.warning('出现错误，找不到上级节点😥')
      }
    }
    // 更新指定节点的dom
    // const nodeDomUpdate = (node: { id: string | number }) => {
    //   // this.$refs[node.id].$forceUpdate();
    // }
    // 给定一个起始节点，遍历内部所有节点
    const forEachNode = (
      parent: any,
      node: ITreeNode,
      callback: {
        (parent: { id: any; }, node: { id: string; parentId: any; }): void
        (arg0: any, arg1: any): void
      }
    ) => {
      if (isBranchNode(node)) {
        callback(parent, node)
        forEachNode(node, node.children, callback)
        node.branchs?.map((branchNode: { children: any; }) => {
          callback(node, branchNode)
          forEachNode(branchNode, branchNode.children, callback)
          return branchNode
        })
      }
      else if (
        isPrimaryNode(node)
        || isEmptyNode(node)
        || isBranchSubNode(node)
      ) {
        callback(parent, node)
        forEachNode(node, node.children, callback)
      }
    }

    const { __ctx } = getCurrentInstance() as any
    const validateNode = (err: any, node: { id: string | number; }) => {
      if (__ctx?.$refs[node.id]?.validate)
        valid.value = __ctx?.$refs[node.id].validate(err)
    }
    // 校验所有节点设置
    const validate = (err: any, node: ITreeNode) => {
      if (isPrimaryNode(node)) {
        validateNode(err, node)
        validate(err, node.children)
      }
      else if (isBranchNode(node)) {
        // 校验每个分支
        node.branchs.map((branchNode: any) => {
          // 校验条件节点
          validateNode(err, branchNode)
          // 校验条件节点后面的节点
          validate(err, branchNode.children)
          return branchNode
        })
        validate(err, node.children)
      }
      else if (isEmptyNode(node)) {
        validate(err, node.children)
      }
    }
    const validateProcess = () => {
      valid.value = true
      const err: never[] = []
      validate(err, dom.value)
      return err
    }

    // 向父组件抛出方法
    expose({ validateProcess })

    return () => {
      console.info('渲染流程树')
      nodeMap.value.clear()
      const processTrees = getDomTree(h, dom.value)
      // 插入末端节点
      processTrees.push(
        h('div', { style: { 'text-align': 'center' } }, [
          h(
            'div',
            {
              class: { 'process-end': true }
              // domProps: { innerHTML: "流程结束" },
            },
            ['流程结束']
          )
        ])
      )
      return h('div', { class: { _root: true }, ref: 'refRoot' }, processTrees)
    }
  }
}
</script>

<style lang="scss" scoped>
._root {
  margin: 0 auto;
}

.process-end {
  width: 85px;
  margin: 0 auto;
  margin-bottom: 20px;
  border-radius: 15px;
  padding: 5px 10px;
  font-size: small;
  color: var(--el-text-color-regular);
  background-color: var(--el-bg-color);
  box-shadow: var(--el-box-shadow-light);
}

.primary-node {
  display: flex;
  align-items: center;
  flex-direction: column;
}

.branch-node {
  display: flex;
  justify-content: center;
}

.branch-node-item {
  position: relative;
  display: flex;
  background: var(--ikflow-bg-color-page);
  flex-direction: column;
  align-items: center;
  border-top: 2px solid var(--el-text-color-disabled);
  border-bottom: 2px solid var(--el-text-color-disabled);

  &::before {
    content: '';
    position: absolute;
    top: 0;
    left: calc(50% - 1px);
    margin: auto;
    width: 2px;
    height: 100%;
    background-color: var(--el-text-color-disabled);
  }

  .line-top-left,
  .line-top-right,
  .line-bot-left,
  .line-bot-right {
    position: absolute;
    width: 50%;
    height: 4px;
    background-color: var(--ik-main-bg);
  }

  .line-top-left {
    top: -2px;
    left: -1px;
  }

  .line-top-right {
    top: -2px;
    right: -1px;
  }

  .line-bot-left {
    bottom: -2px;
    left: -1px;
  }

  .line-bot-right {
    bottom: -2px;
    right: -1px;
  }
}

.add-branch-btn {
  position: absolute;
  width: 88px;

  .add-branch-btn-el {
    z-index: 999;
    position: absolute;
    top: -15px;
  }
}

.empty-node {
  display: flex;
  justify-content: center;
  flex-direction: column;
  align-items: center;
}
</style>
